/*
 * nmats.c
 *
 *  Created on: Jan 11, 2010
 *      Author: Marco Luisi
 */

#include "nmats.h"

user* users;

FILE *list_fd;
// volatile per i sighandler
int active_users;
int login_pipe;
int login_w_pipe;
int users_num;
int workers; //o active_users

DIR * datadir = NULL;


int main(int argc, char **argv) {

	//signalhandlers

	//da riscrivere
//	struct sigaction chld_action;
//	bzero(&chld_action, sizeof(chld_action));
//	chld_action.sa_handler = chld_handler;
//	sigaction(SIGCHLD,&chld_action,NULL);

	struct sigaction int_action; //serve gestirlo o ignorarlo?
	bzero(&int_action, sizeof(int_action));
	int_action.sa_handler = int_handler;
	sigaction(SIGINT,&int_action,NULL);

	struct sigaction term_action;
	bzero(&term_action, sizeof(term_action));
	term_action.sa_handler = term_handler;
	sigaction(SIGTERM,&term_action,NULL);

	if (argc != 2) {
		WRITELN("NMAT Server: Errore! Fornire una lista utenti!");
		WRITELN("NMAT Server: Digitare 'nmats nomelista' ");
		WRITELN("NMAT Server in uscita!");
		exit(-1);
	}
	/* Apertura del file lista utenti */
	//spostare in init?
	EC_NULL_E(list_fd = fopen(argv[1], "r"), "NMAT Server: errore nell'apertura lista utenti");
	//inizializzazione server
	printf("avvio init \n");
	init();
	//andare in background, qui o già dentro init
	WRITELN("FINE INIT");
	//stampa log sugli utenti
	int j;
	for(j = 0; j<users_num; j++) { //poi togliere, solo per controllo ora
		WRITELN(users[j].name);
	}

	listen();
	WRITELN("FINE listen");
	return 0;
}

void init() {

	//anticipare la gestione directory?
	int i;
	int to_client;
	int list_dim = 5; //macro?
	users_num = 0;
	char* buffer; //dare qui già la dim con una macro?
	char* name_string;
	printf("dentro init  \n");
	unlink("./clientserver");//ma fare in modo di metterla nella cleanup?
	EC_M1_E(mkfifo("clientserver",0666),"NMAT Server: errore nella creazione della pipe");
	//rivedere umask e riprovare con RW e NONBLOCK
	EC_M1_E(login_pipe = open("clientserver", O_RDONLY),"NMAT Server: errore apertura loginp pipe");
	EC_M1_E(login_w_pipe = open("clientserver", O_WRONLY),"NMAT Server: errore apertura loginp pipe");
	//per evitare EOF!
	WRITELN("NMAT Server: login pipe aperta");


	//decidersi per le dimensioni e le macro
	/**
	 *  Parsing della lista utenti
	 */
	EC_NULL_E(buffer = malloc(10 * sizeof(char)),"NMAT Server errore di allocazione memoria.");
	EC_NULL_E(name_string = malloc(NAME_LEN * sizeof(char)),"NMAT Server errore di allocazione memoria.");
	EC_NULL_E(users = malloc(list_dim * sizeof(user)),"NMAT Server errore di allocazione memoria.");

	while (fgets(buffer, (NAME_LEN +1), list_fd) != NULL) { //rivedere il valore
		//se si raggiunge il bound dell'array, si chiama realloc per ingrandirlo
		if (users_num == list_dim) {
			list_dim *= 2; //raddoppia la dimensione array
			EC_NULL_E(users = (user*)realloc(users, list_dim * sizeof(user)), "NMAT Server: errore di memoria" );

		}

		if (sscanf(buffer, "%s", name_string) != -1) { //funzioncina per adduser?
			user temp; //malloc?
			//ma andrà bene cosi??
			//con strdup non serve mallocare prima?
			//temp.name = calloc((strlen(name_string) + 1), sizeof(char)); //calloc e +1
			temp.name = strdup(name_string);
			temp.wpid = 0;
			users[users_num] = temp;
			users_num++;
		}

	} // finito di leggere il file

	fclose(list_fd);//error check
	//ora ho l'array di user, da ordinare
	EC_NULL_E(users = realloc(users, users_num * sizeof(user)),"NMAT Server ");
	qsort(users, users_num, sizeof(user), usrcmp);

	//gestione directory

	//vecchio codice
	//struct dirent *ep; //rinominare e spostare
	datadir = NULL; //serve?
	datadir = opendir("nmat_data"); // imparare i permessi...
	perror("creazione directory:");
	if (datadir != NULL) {

		WRITELN("Directory di lavoro aperta.");
	} else {
		makedir("nmats_data"); // la macroooo
		WRITELN("Directory di lavoro creata.");
	}

	// creazione directory nmats
	//ritoccare!!!!
	int check;
	check = chdir("./nmats_data");
	printf("chdir = %d\n",check);
	if ( check == -1) { //capire perchè wdir definito senza " no nva
		perror("chdir");
		exit(errno);
	}
	//fine vecchio codice
//	datadir = NULL;
//	datadir = opendir("nmat_data");
//	if (datadir != NULL) {
//			WRITELN("Directory di lavoro aperta.");
//		} else {
//			makedir("nmats_data");
//			WRITELN("Directory di lavoro creata.");
//			//datadir = opendir("nmat_data");
//		}
//	//makedir("nmat_data");
//	if (chdir("nmats_data") == -1) { //capire perchè wdir definito senza " no nva
//			perror("chdir");
//			exit(errno);
//		}

	//cambiare prima dir in nmat_data come nella vecchia versione
	//creazione dir per ogni utente
	for (i = 0; i < users_num; i++) {
		makedir(users[i].name); //pare non aprirle
	}

}

void listen() {
	char *buffer;
	char *cpid;
	char *login_msg;
	char *out;
	char *response;
	char *username;
	int auth; // = 0 ?
	int bytes;
	int to_client;
	int outc;

	user temp; //tornarci su...
	//user *temp = malloc(sizeof(user));
	// affinare dimensioni buffer
	// allocare staticamente?
	//out = malloc(30 * sizeof(char)); ?!?!
	login_msg = malloc(30 * sizeof(char));
	buffer = malloc(40 * sizeof(char));
	response = malloc(30 * sizeof(char));
	out = malloc(30 * sizeof(char));
	cpid = malloc(8 * sizeof(char)); //ricontrollare
	//loggare l'inizio del ciclo
	//mettere una guardia nello while o ciclo infinito?
	while(1){
		//bytes = 0;
		//temp = NULL;
		printf("DEBUG listen: inizio ciclo\n");
		EC_M1_E(bytes = read(login_pipe, buffer,30),"NMAT SERVER ERRORE READ INPUT PIPE");
		//printf("DEBUG- listen: dopo read letti %d bytes \n", bytes);

		if (bytes > 0) {  //direttamente bytes || cmq vedere di farlo più elegante
			//printf("DEBUG: buffer dopo read = !%s!\n",buffer);
			//WRITELN(buffer); //nel log
			//printf("DEBUG- listen: dopo read letti %d bytes \n", bytes);
				outc = snprintf(login_msg, (size_t)bytes+1 , "%s", buffer); //nb non conta \0
				cpid = strtok(login_msg, " "); // controllo se numero?
				username = strtok(NULL, " ");
				if(strcmp(username, "CONNECT") != 0){//se messaggio difforme dal protocollo
					//errore nel log!
					WRITELN("errore msg login");
				//come fare?
			}
			username = strtok(NULL, " ");
			username = strcat(username, "\0");
			//WRITE nel log "login di username"
			sprintf(out,"%s%s", "../serverclient",cpid);
			//printf("DEBUG: serverclient = !%s!\n",out);
			to_client = open(out, O_WRONLY);// EC
			perror("open out");
			//printf("DEBUG: to client = !%d!\n",to_client);
			auth = validate(username,&temp);
			printf("DEBUG: auth = !%d!\n",auth);
			//la dimensione è sempre la stessa.. considerarlo?
			//passare a sprintf e mettere EC
			bytes = sprintf(response,"%s%d","LOGIN ",auth);// riuso di bytes ok? - sprintnf
			EC_M1_E(write(to_client,response,bytes), "asd"); //meglio rispondere dopo la fork?

			if (auth) { //se autenticato, fork del figlio e aggiornamento status utente
				printf("DEBUG: INIT prima di spawn temp.name=!%s! cpid=!%s!\n",temp.name,cpid);
				spawn(&temp, cpid);//passare user?
			}




		}// fine if read

	printf("DEBUG: fine ciclo while listen\n");
	}


} // fine listen


int usrcmp(const void *n1, const void *n2) {
	user *x1 = (user *) n1;
	user *x2 = (user *) n2;
	return (strcmp(x1->name, x2->name));
}


void spawn(user *user, char* cpid){
	int wpid;
	char *dir;
	dir = malloc(PATH_MAX * sizeof(char));
	dir = getcwd(dir,PATH_MAX); //non standard!
	EC_M1_E(wpid = fork(),"NMAT Server errore spawn worker"); // o meglio F con una funz che risponda al client comunicando errore
	if (wpid == 0) { //pid == 0 || figlio
		EC_M1_E(execl("../worker","worker", user->name,cpid,(char*)NULL), "NMAT Server errore spawn worker");
		//dA CAMBIiare da _E a _F ??
	}
	else { //si presuppone il -1 gestito dentro la macro
		// aggiornare info del worker
		printf("DEBUG SERVER dopo spawn!!!\n");
		user->wpid = wpid;
		workers++;

	}

	//chiusura dir?

}




//attenzione prima restituiva l'indice nell'array
// -1 = NOME ERRATO || 2 GIA' LOGGATO || 1 OK

// semplificare
int validate(char *username, user *res){ //rivedere come restituire la struct
	user key;
	user *temp;
	key.name = strdup(username);
	key.wpid = 0; //vedere se serve o meno
	temp = (user*)bsearch(&key,users,users_num,sizeof(user),usrcmp);
	if (temp == NULL){ //utente non presente nella lista
		//printf("DEBUG: VALIDATE res = NULL\n");
		return -1;  //log?
	}
	printf("DEBUG: VALIDATE temp.name = !%s! res.pid = !%d!\n",temp->name,temp->wpid);
	if (temp->wpid){ //utente già connesso al sistema
		//printf("DEBUG: VALIDATE res gia connesso\n");
		return 2;
	}
	printf("DEBUG: VALIDATE autenticato!\n");
	res->name = strdup(username);
	return 1; //autenticazione riuscita
}


//
void makedir(char* dir_name) { //restituire *DIR?

	//	dir = NULL; //spostare fuori? e cmq serve?
	//	EC_NULL_F(dir = opendir(dir_name), EC_M1_E( mkdir(dir_name, O_CREAT | 0666), "NMAT Server: errore su op file" ) );
	//	EC_NULL_E(dir = opendir(dir_name), "asd");
	if (mkdir(dir_name, O_CREAT | 0666) == -1) {
		if (errno == EEXIST)
			return; //magari nel log dir presente..
		else {
			perror("asd");
			exit(errno);
		}
	}
	WRITE("Creata directory ");
	WRITELN(dir_name);
}

//Signal handlers
// NB: togliere le prinft dai sighandler

//void chld_handler(int signum) {
//	pid_t childpid;
//	int i;
//	int y;
//	childpid = wait(NULL);
//	printf("NMAT Server: catturato sigchld dal worker %d\n",(int)childpid);
//
//	//nella vecchia versione array con i pid degli workers
//	//scorre l'array(non ordinato) di worker
//	for (i = 0; i< active_workers; i++) {
//				printf("FOR su worker, ciclo %d\n", i);
//			if(childpid == workers[i].wpid) {
//				printf("trovato in worker[%d]:utente=%s,pid=%d\n",i,workers[i].user,workers[i].wpid);
//				for (y = 0; y < usersnum; y++){
//					printf("FOR su users, ciclo %d\n", y);
//					if( strcmp(workers[i].user, users[y].name) == 0) {
//						printf("USER[%d] name = %s status=%d\n",y , users[y].name,users[y].status);
//						printf("workers[%d] per user=%s, wpid=%d\n", i, workers[i].user, workers[i].wpid);
//						users[y].status = 0;
//
//					}
//				} // fine for sugli utenti
//			}// fine for sugli workers
//		}
//	} // fine chld_handler

void int_handler(int signum){ // uguale a term_handler?

	close(login_pipe);
	//EC_M1(close(login_pipe)); //che fare se da errore qui?
	unlink("../clientserver");
	kill(0, SIGTERM);
	while(waitpid (-1, NULL, 0)) {
		if (errno == ECHILD){
			printf("tutti gli worker terminati");
		break;
		}
	}
	//cleanup
	exit(0);
}

void term_handler(int signum) {

	close(login_pipe);
	//EC_M1(close(login_pipe)); //che fare se da errore qui?
	unlink("../clientserver");
	kill (0,SIGTERM);
	while( waitpid(0,NULL,0) ) {
		if(errno == ECHILD) {
			printf("tutti gli worker terminati");
		break;

		}
	}

	exit(0); //cleanup?
}
